package datatypes;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Set;
import java.util.Stack;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;

import controller.DraftingHint;
import controller.Globals;
import controller.Notification;
import controller.NotificationCenter;
import datatypes.kdTree.EdgeContainsVertexCondition;
import datatypes.kdTree.KdTree;
import datatypes.kdTree.Range;
import datatypes.kdTree.TriangleContainsVertexCondition;
import datatypes.list.DoublyLinkedList;

/**
 * A collection class to keep track of all faces needed during triangulation. The faces include:<br>
 * 1. All vertices<br>
 * 2. candidateEdges<br>
 * 3. edgesIn<br>
 * 4. candidateTriangles<br>
 * 5. betaCircles (if beta skeleton is enabled)<br>
 * <br>
 * Provided are also methods to efficiently operate on the face set.
 * 
 * @author nils
 *
 */
public class FaceSet implements List<Vertex> {
	
	/**
	 * This factor is used in {@link #diamondPeakVectorForEdge(Edge)}
	 * to calculate the 3rd vertex of the triangles used for
	 * testing the diamond property of the candidate edges
	 * {@link #diamondTest(Edge)}.
	 * This factor is calculated as follows: sin(diamondAngle)/(2*sin(PI/2 - diamondAngle)).
	 * 
	 * diamond property from: Drysdale, McElfresh, Snoeyink: On Exclusion Regions for Optimal Triangulations
	 * via: Giri Narasimhan,Michiel Smid: Geometric spanner networks
	 */
	private double DIAMOND_FACTOR = 0.38462166912;
	
	/**
	 * sqrt((beta^2-1)/4); beta = 1.17682
	 */
	private static final double SECRET_MAGIC_BETA_SKELETON_FACTOR = 0.31020368807;
	private static final double BETA_HALF = 0.58814;
	
	/**
	 * Stores all vertices allowing for fast rectangular range searching.
	 */
	public KdTree vertices;
	
	/**
	 * Holds all edges that are candidates for the triangulation.
	 */
	private DoublyLinkedList<Edge> candidateEdges;
	
	/**
	 * All triangles that are candidates for an MWT.
	 */
	private List<Triangle> candidateTriangles;
	
	/**
	 * All edges that have been found to be element of the LMT skeleton.
	 * Initially this list is empty.
	 */
	public List<Edge> edgesIn = new ArrayList<Edge>();
	
	/**
	 * All edges that have been found not to be element of the LMT skeleton but element of the MWT.
	 * Initially this list is empty.
	 */
	public List<Edge> edgesLMTHole = new ArrayList<Edge>();
	
	/**
	 * If the beta-skeleton is constructed, this list will contain the circles
	 * used to positively identify the edges of the beta-skeleton. These circles
	 * may be used for display ({@link Gui.TriangleDisplay#toggleShowCircles()}
	 */
	public List<Circle> betaCircles = new ArrayList<Circle>();
	
	/**
	 * Flag for enabling testing for the diamond property.
	 */
	private boolean diamondTestEnabled = true;
	
	/**
	 * Flag for enabling the beta skeleton.
	 */
	private boolean betaSkeletonEnabled = true;
	
	/**
	 * Flag for aborting a concurrently running operation.
	 */
	private boolean aborted = false;
	
	public FaceSet(double diamondAngle, boolean useBetaSkeleton, Vertex[] vertices) {
		this.betaSkeletonEnabled = useBetaSkeleton;
		Edge.edgeCounter = 0;
		if (vertices != null && vertices.length > 0) {
			for (int i = 0; i < vertices.length; i++) {
				vertices[i].index = i;
				vertices[i].clearAdjacentEdges();
			}
			this.vertices = new KdTree(vertices, vertices[0].getDimension());
		}
		this.candidateEdges = new DoublyLinkedList<Edge>();
		this.candidateTriangles = new ArrayList<Triangle>();
		this.diamondTestEnabled = (diamondAngle > 0);
		if (diamondTestEnabled) {
			// sin(PI/4.6)/(2*sin(PI/2 - PI/4.6))
			this.DIAMOND_FACTOR = Math.sin(diamondAngle) / (2 * Math.sin(Math.PI/2 - diamondAngle));
		}
	}
	
	/**
	 * @return the number of candidate triangles.
	 */
	public int candidateTriangleCount() {
		return candidateTriangles.size();
	}
	
	public DoublyLinkedList<Edge> getCandidateEdges() {
		return candidateEdges;
	}
	
	/**
	 * Removes an edge from the adjacentEdges list.
	 * All candidate triangles with this edge will also be removed.
	 * @param edge The edge to be removed.
	 * @return true if edge has been removed successfully.
	 */
	public boolean removeEdge(Edge edge) {
		boolean removed = removeEdgeButLeaveTriangles(edge);
		List<Triangle> triangles = edge.getTrianglesLeft();
		for (Triangle triangle : triangles) {
			this.candidateTriangles.remove(triangle);
			Edge edge1 = triangle.getEdge(2, edge);
			Edge edge2 = triangle.getEdge(3, edge);
			
			edge1.removeTriangleFromAdjacentTriangles(triangle);
			edge2.removeTriangleFromAdjacentTriangles(triangle);
		}
		triangles = edge.getTrianglesRight();
		for (Triangle triangle : triangles) {
			this.candidateTriangles.remove(triangle);
			Edge edge1 = triangle.getEdge(2, edge);
			Edge edge2 = triangle.getEdge(3, edge);
			
			edge1.removeTriangleFromAdjacentTriangles(triangle);
			edge2.removeTriangleFromAdjacentTriangles(triangle);
		}
		return removed;
	}
	
	/**
	 * Removes an edge from the adjacentEdges list but does not remove
	 * any triangles.
	 * @param edge The edge to be removed.
	 * @return true if edge has been removed successfully.
	 */
	public boolean removeEdgeButLeaveTriangles(Edge edge) {
		boolean removed = this.candidateEdges.remove(edge);
		List<Edge> adjacentEdgesV1 = edgesContainingVertex(edge.getVertex1());
		List<Edge> adjacentEdgesV2 = edgesContainingVertex(edge.getVertex2());
		
		if ( !adjacentEdgesV1.remove(edge)) {
			removed = false;
		}
		if ( !adjacentEdgesV2.remove(edge)) {
			removed = false;
		}
		return removed;
	}
	
	/**
	 * Remove triangle from candidateTriangles.
	 * Triangle will also be removed from each edge's adjacentTriangles.
	 * @param triangle
	 * @return true if successful.
	 */
	public boolean removeTriangle(Triangle triangle) {
		boolean removed = this.candidateTriangles.remove(triangle);
		
		Edge edge1 = triangle.getEdge1();
		Edge edge2 = triangle.getEdge2();
		Edge edge3 = triangle.getEdge3();
		
		edge1.removeTriangleFromAdjacentTriangles(triangle);
		edge2.removeTriangleFromAdjacentTriangles(triangle);
		edge3.removeTriangleFromAdjacentTriangles(triangle);
		
		return removed;
	}
	
	/**
	 * Returns a Set of edges that contain the given Vertex.
	 * Runs in O(1) time.
	 * @param vertex
	 * @return
	 */
	public List<Edge> edgesContainingVertex(Vertex vertex) {
		return vertex.getAdjacentEdges();
	}
	
	// Used by PolygonTriangulator for the cycleSearch
	public Set<Edge> getEdgesInContainingVertex(Vertex vertex)
	{
		Set<Edge> result = new HashSet<Edge>();
		for (int i = 0; i < edgesIn.size(); i++)
		{
			Edge e = edgesIn.get(i);
			if (e.getVertex1() == vertex || e.getVertex2() == vertex) result.add(e);
		}
		return result;
	}

	/**
	 * Calculates a vector necessary for determining the missing vertex for the triangle for the diamond test.
	 * The vertex can be determined by translating the returned vector to the midpoint of the edge.
	 * The peak vertex on the other side can be obtained by multiplying the returned vector by -1 before translating.
	 * The edge's vertices provide the other two vertices.
	 * @param edge
	 * @return
	 */
	Vertex diamondPeakVectorForEdge(Edge edge) {
		Vertex normal = edge.getNormal();
		double normalLength = normal.norm();
		double height = edge.getLength() * DIAMOND_FACTOR;
		double factor = height / normalLength;
		// make sure the length is correct:
		return normal.multiplyWithScalar(factor);
	}
	
	/**
	 * Returns the vector of the center of the edge to the center
	 * of one of the circles used to define the beta-skeleton. Invert
	 * this vector to determine the center of the other circle.
	 * In order to get the actual center Vertices of the beta circles,
	 * translate the center of the edge by this vector.
	 * @param edge
	 * @return
	 */
	Vertex betaCircleMidpointVectorForEdge(Edge edge) {
		Vertex normal = edge.getNormal();
		double normalLength = normal.norm();
		double height = edge.getLength() * SECRET_MAGIC_BETA_SKELETON_FACTOR;
		double factor = height / normalLength;
		// make sure the length is correct:
		return normal.multiplyWithScalar(factor);
	}
	
	/**
	 * Returns true if the edge passes the diamondTest.
	 * @param edge
	 * @param condition An object used by the kd-tree. This method will
	 * package a triangle into this condition, the kd-tree then decides whether
	 * the triangle is empty. This condition object is passed into this method
	 * in order to re-use it, avoiding the overhead of creating a fresh object each time.
	 * @return
	 */
	private boolean diamondTest(Edge edge, TriangleContainsVertexCondition condition) {
		Vertex peak1 = diamondPeakVectorForEdge(edge);
		Vertex peak2 = peak1.multiplyWithScalar(-1);
		
		Vertex center = edge.getCenter();
		
		// translate back into position:
		peak1.translate(center);
		peak2.translate(center);
		
		Vertex v1 = edge.getVertex1();
		Vertex v2 = edge.getVertex2();
		
		condition.setTriangle(v1, v2, peak1);
		boolean t1 = Triangle.isEmptyWithPretest(this, condition);
		condition.setVertex3(peak2);
		boolean t2 = Triangle.isEmptyWithPretest(this, condition);
		return t1 || t2;
		
	}
	
	/**
	 * Checks whether this edge is member of the beta skeleton.
	 * True if the two beta circles of the edge are empty.
	 * The beta circles are the two circles of radius (1.17682 times
	 * half the length of the edge) that have the edge's two endpoints
	 * on their circumferences.
	 * 
	 * For visualization purposes the circles of beta edges are added
	 * to the circles list.
	 * @param edge
	 * @return
	 */
	private boolean isInBetaSkeleton(Edge edge) {
		double radius = edge.getLength() * BETA_HALF;
		
		Vertex betaMidpoint1 = betaCircleMidpointVectorForEdge(edge);
		Vertex betaMidpoint2 = betaMidpoint1.multiplyWithScalar(-1);
		
		Vertex center = edge.getCenter();
		
		// translate back into position:
		betaMidpoint1.translate(center);
		betaMidpoint2.translate(center);
		
		double minX = Math.min(betaMidpoint1.getX(), betaMidpoint2.getX())-radius;
		double maxX = Math.max(betaMidpoint1.getX(), betaMidpoint2.getX())+radius;
		double minY = Math.min(betaMidpoint1.getY(), betaMidpoint2.getY())-radius;
		double maxY = Math.max(betaMidpoint1.getY(), betaMidpoint2.getY())+radius;
		Range range = new Range(minX, maxX, minY, maxY);
		try {
			Future<List<Vertex>> candidateVerticesFuture = vertices.search(range);
			List<Vertex> candidateVertices = candidateVerticesFuture.get();
			for (Vertex vertex : candidateVertices) {
				if (vertex.distance(betaMidpoint1) < radius ||
					vertex.distance(betaMidpoint2) < radius) {
					// vertex is in one of the beta circles!
					
					return false;
				}
			}
		} catch (InterruptedException e) {
			System.out.println("InterruptedException");
			return false;
		} catch (ExecutionException e) {
			System.out.println("ExecutionException");
			return false;
		}
		
		betaCircles.add(new Circle(betaMidpoint1, radius));
		betaCircles.add(new Circle(betaMidpoint2, radius));
		return true;
	}
	
	/**
	 * Concurrently generates all possible edges.
	 * Depending on the flag {@link #diamondTestEnabled}, only edges passing
	 * {@link #diamondTest(Edge)} are added to candidate edges.
	 */
	public void generateEdges() {
		Stack<Future<List<Edge>>> futures = new Stack<Future<List<Edge>>>();
		for (int i = 0; i < vertices.size(); i++) {
			Vertex v1 = vertices.get(i);
			futures.push(edgesPassingDiamondTest(v1, i));
		}
		for (Future<List<Edge>> future : futures) {
			try {
				List<Edge> edges = future.get();
				for (Edge edge : edges) {
					addEdge(edge);
					if (betaSkeletonEnabled && isInBetaSkeleton(edge)) {
						edge.setStatus(DraftingHint.BETA_SKELETON);
						edgesIn.add(edge);
					}
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			} catch (ExecutionException e) {
				e.printStackTrace();
			}
		}
		if (betaSkeletonEnabled) {
			NotificationCenter nc = NotificationCenter.defaultNotificationCenter();
			nc.postNotification(Notification.didFindCircles, betaCircles);
		}
		System.out.println("generated " + candidateEdges.size() + " edges");
	}
	
	private boolean edgeIsEmpty(Edge edge, EdgeContainsVertexCondition condition) {
		return edge.isEmpty(this, condition);
	}
	
	/**
	 * Investigates for the given vertex whether the edges between the
	 * given vertex and all other vertices beginning at the given index
	 * pass the diamond test {@link #diamondTest(Edge)}. The edges passing
	 * the test are returned to the Future.
	 * @param vertex
	 * @param index
	 * @return
	 */
	private Future<List<Edge>> edgesPassingDiamondTest(final Vertex vertex, final int index) {
		ExecutorService executer = Globals.mainExecutor;
		Future<List<Edge>> future = executer.submit(new Callable<List<Edge>>() {
			public List<Edge> call() throws Exception {
				List<Edge> foundEdges = new ArrayList<Edge>();
				/*
				 *  Creating a condition object.
				 *  This condition object checks whether a vertex is inside a triangle.
				 *  In order to avoid having to create a new condition object for each
				 *  triangle, we generate an empty one here and re-use it for each
				 *  triangle by passing it as a parameter to the diamondTest. The 
				 *  diamondTest method will provide the triangle.
				 */
				TriangleContainsVertexCondition diamondCondition = new TriangleContainsVertexCondition(null, null, null);
				EdgeContainsVertexCondition edgeEmptyCondition = new EdgeContainsVertexCondition(null);
				for (int j = index+1; j < vertices.size(); j++) {
					if (aborted) {
						return foundEdges;
					}
					Vertex v2 = vertices.get(j);
					Edge edge = new Edge(vertex, v2);
					if (!diamondTestEnabled || diamondTest(edge, diamondCondition)) {
						if (edgeIsEmpty(edge, edgeEmptyCondition)) {
							edge.setStatus(DraftingHint.CONVEX_HULL);
							foundEdges.add(edge);
						}
					}
				}
				return foundEdges;
			}
		});
		return future;
	}
	
	/**
	 * Generates all possible triangles for the vertices in the {@link #vertices} List.
	 * Will not create duplicates. Triangles will be stored in this
	 * class's {@link #candidateTriangles} List. Triangles will also be added to each edge's
	 * {@link datatypes.Edge#adjacentTriangles} Set for fast access.
	 */
	public void generateTriangles() {
		Set<Edge> triangleEdges = new HashSet<Edge>();
		for (Edge edge : candidateEdges) {
			addTrianglesForEdge(edge, triangleEdges);
		}
		if (betaSkeletonEnabled) {
			for (Edge edge : candidateEdges) {
				if (edge.getStatus() == DraftingHint.BETA_SKELETON) {
					removeEdgeButLeaveTriangles(edge);
				}
			}
		}
	}

	/**
	 * @param vertex
	 * @param otherVertex
	 * @param edge
	 */
	private void addEdge(Edge edge) {
		candidateEdges.add(edge);
		Vertex v1 = edge.getVertex1();
		Vertex v2 = edge.getVertex2();
		v1.getAdjacentEdges().add(edge);
		v2.getAdjacentEdges().add(edge);
	}	
	
	/**
	 * Adds all triangles for the given edge.
	 * Each triangle will also be added to the triangle's two other
	 * edges. The triangleEdges set is used to determine which triangles
	 * have already been added to this IndexedFaceSet by previous calls
	 * to this method. Therefore the calling method has to keep track
	 * of the triangleEdges set. See {@link #generateTriangles()}.
	 * @param edge
	 * @param triangleEdges
	 */
	private void addTrianglesForEdge(Edge edge, Set<Edge> triangleEdges) {
		
		for (Edge adjacentEdge1 : edge.getVertex1().getAdjacentEdges()) {
			
			// Make sure the adjacentEdge is in fact a different edge.
			// Then make sure the triangles for the adjacentEdges have not
			// been added yet.
			if (adjacentEdge1 == edge || triangleEdges.contains(adjacentEdge1)) {
				continue;
			}
			for (Edge adjacentEdge2 : edge.getVertex2().getAdjacentEdges()) {
				
				// Same here (see above comment).
				if (adjacentEdge2 == edge) {
					continue;
				}
				if (adjacentEdge1.isAdjacentToEdge(adjacentEdge2)) {
					if (triangleEdges.contains(adjacentEdge2)) {
						continue;
					}
					// Yay! All three edges form a triangle!!!
					try {
						Triangle triangle = new Triangle(edge, adjacentEdge1, adjacentEdge2, false);
						if (triangle.isEmpty(this, 0.000000001)) {
							candidateTriangles.add(triangle);
							edge.addTriangleToAdjacentTriangles(triangle);
							adjacentEdge1.addTriangleToAdjacentTriangles(triangle);
							adjacentEdge2.addTriangleToAdjacentTriangles(triangle);
						}
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			}
		}
		// by adding this edge  we will know in consecutive calls
		// that all triangles for this edge have been added before.
		triangleEdges.add(edge);
	}
	
	public Iterable<Vertex> vertexRangeIterable(Range range) {
		return vertices.rangeIterator(range);
	}

	/**
	 * Operation not supported!
	 */
	@Override
	public void add(int index, Vertex element) {
		// nope, can't do that because that would mess up the indices!!!
		throw new UnsupportedOperationException();
	}
	
	@Override
	public boolean addAll(Collection<? extends Vertex> c) {
		boolean result = true;
		for (Vertex vertex : c) {
			if (!add(vertex)) {
				result = false;
			}
		}
		return result;
	}
	
	/**
	 * Operation not supported!
	 */
	@Override
	public boolean addAll(int index, Collection<? extends Vertex> c) {
		// nope, can't do that because that would mess up the indices!!!
		throw new UnsupportedOperationException();
	}
	
	/**
	 * Unsupported operation
	 */
	@Override
	public void clear() {
		throw new UnsupportedOperationException();
	}
	
//	/**
//	 * Removes all vertices and faces. Resets indices of all vertices to -1.
//	 * WARNING: Time O(n) operation!
//	 */
//	@Override
//	public void clear() {
//		for (Vertex vertex : this) {
//			vertex.index = -1;
//		}
//		vertices.clear();
//		numberOfVertices = 0;
//	}
	
	/**
	 * Unsupported operation
	 */
	@Override
	public Vertex remove(int index) {
		throw new UnsupportedOperationException();
	}
	
//	/**
//	 * Removes and returns the Vertex at the specified position.
//	 * Updates all changed indices.
//	 * WARNING: Time O(n + e + t) operation with n = this.size() - index, e the number of edges to be removed, t the number of triangles to be removed.
//	 */
//	@Override
//	public Vertex remove(int index) {
//		Vertex removed = vertices.remove(index);
//		// update indices
//		for (int i = index; i < this.size(); i++) {
//			Vertex v = this.get(i);
//			v.index = i;
//		}
//		// remove edges:
//		for (Edge edge : removed.getAdjacentEdges()) {
//			candidateEdges.remove(edge);
//			Vertex otherVertex = edge.getOtherVertex(removed);
//			otherVertex.getAdjacentEdges().remove(edge);
//			for (Triangle triangle : edge.getTrianglesLeft()) {
//				triangles.remove(triangle);
//				Edge otherEdge1 = triangle.getEdge(2, edge);
//				Edge otherEdge2 = triangle.getEdge(3, edge);
//				otherEdge1.removeTriangleFromAdjacentTriangles(triangle);
//				otherEdge2.removeTriangleFromAdjacentTriangles(triangle);
//			}
//			for (Triangle triangle : edge.getTrianglesRight()) {
//				triangles.remove(triangle);
//				Edge otherEdge1 = triangle.getEdge(2, edge);
//				Edge otherEdge2 = triangle.getEdge(3, edge);
//				otherEdge1.removeTriangleFromAdjacentTriangles(triangle);
//				otherEdge2.removeTriangleFromAdjacentTriangles(triangle);
//			}
//		}
//		return removed;
//	}
	
	/**
	 * Removes the Vertex if present.
	 * Updates all changed indices.
	 * WARNING: Time O(n) operation with n = this.size() - v.index!
	 */
	@Override
	public boolean remove(Object v) {
		if (v.getClass() != Vertex.class) {
			return false;
		}
		Vertex vertex = (Vertex) v;
		if (vertex.index >= 0) {
			remove(vertex.index);
			return true;
		}
		return false;
	}
	
	/**
	 * Operation not supported!
	 */
	@Override
	public boolean removeAll(Collection<?> c) {
		// nope, can't do that because that would mess up the indices!!!
		throw new UnsupportedOperationException();
	}
	
	/**
	 * Operation not supported!
	 */
	@Override
	public boolean retainAll(Collection<?> c) {
		// nope, can't do that because that would mess up the indices!!!
		throw new UnsupportedOperationException();
	}
	
	/**
	 * Operation not supported!
	 */
	@Override
	public Vertex set(int index, Vertex vertex) {
		// nope, can't do that because that would mess up the indices!!!
		throw new UnsupportedOperationException();
	}
	
	/**
	 * Returns true if this list contains the specified element.
	 * O(1) time operation :-)
	 * @param elem element whose presence in this List is to be tested.
	 * @return true if the specified element is present; false otherwise.
	 */
	@Override
	public boolean contains(Object elem) {
		if (elem.getClass() != Vertex.class) {
			return false;
		}
		Vertex vertex = (Vertex) elem;
		boolean contains = false;
		if (vertex.index >= 0 && vertex.index < this.size()) {
			if (this.get(vertex.index) == vertex) {
				contains = true;
			}
		}
		return contains;
	}

	@Override
	public boolean containsAll(Collection<?> arg0) {
		// TODO Auto-generated method stub
		throw new UnsupportedOperationException();
//		return false;
	}

	@Override
	public Vertex get(int index) {
		return vertices.get(index);
	}

	@Override
	public int indexOf(Object arg0) {
		// TODO Auto-generated method stub
		throw new UnsupportedOperationException();
//		return 0;
	}

	@Override
	public boolean isEmpty() {
		return vertices.isEmpty();
	}

	@Override
	public Iterator<Vertex> iterator() {
		return vertices.iterator();
	}

	@Override
	public int lastIndexOf(Object arg0) {
		// TODO Auto-generated method stub
		throw new UnsupportedOperationException();
//		return 0;
	}
	
	/**
	 * Unsupported operation
	 */
	@Override
	public ListIterator<Vertex> listIterator() {
		throw new UnsupportedOperationException();
	}

	/**
	 * Unsupported operation
	 */
	@Override
	public ListIterator<Vertex> listIterator(int index) {
		throw new UnsupportedOperationException();
	}

//	@Override
//	public ListIterator<Vertex> listIterator() {
//		return vertices.listIterator();
//	}
//
//	@Override
//	public ListIterator<Vertex> listIterator(int index) {
//		return vertices.listIterator(index);
//	}

	@Override
	public int size() {
		return vertices.size();
	}
	
	@Override
	public List<Vertex> subList(int fromIndex, int toIndex) {
		throw new UnsupportedOperationException();
	}

//	@Override
//	public List<Vertex> subList(int fromIndex, int toIndex) {
//		return vertices.subList(fromIndex, toIndex);
//	}

	@Override
	public Object[] toArray() {
		return vertices.geometry;
	}

	
	public Vertex[] toArray(Vertex[] array) {
		return vertices.geometry;
	}

	/**
	 * Unsupported operation
	 */
	@Override
	public boolean add(Vertex arg0) {
		throw new UnsupportedOperationException();
	}

	/**
	 * Unsupported operation
	 */
	@Override
	public <T> T[] toArray(T[] arg0) {
		throw new UnsupportedOperationException();
	}

	public void abort() {
		aborted  = true;
	}
}
